package com.leador.map.location.serviceserver;

import android.app.Service;
import android.content.Intent;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.SparseArray;

import com.leador.map.location.ILocationService;
import com.leador.map.location.LocationInfo;
import com.leador.map.location.LocationServiceFactory;
import com.leador.map.location.util.Utils;

import org.json.JSONObject;

/**
 *
 */
public class ServiceServer extends Service {
    private static ILocationService locService = null;
    private static Handler handler = null;
    private Location loc = null;
    private Runnable rLoc = null;
    private int iLocNum = 0;
    private int iBindNum = 0;
    private RemoteCallbackList<ICallBack> rcListICallBack = new RemoteCallbackList<ICallBack>();
    private SparseArray<Long> saMin = new SparseArray<Long>();
    private IServiceServer.Stub stub = new IServiceServer.Stub() {
        /**
         * 请求位置更新
         *
         * @param lMin
         * @param iCallBack
         * @return 无
         */
        @Override
        public void requestUpdates(long lMin, ICallBack iCallBack)
                throws RemoteException {
            if (iCallBack == null) {
                return;
            }
            if (rcListICallBack.register(iCallBack)) {
                locService.setUseCache(true);
                int iCallBackId = iCallBack.asBinder().hashCode();
                Utils.writeCat("server|register callback ", iCallBackId);
                saMin.put(iCallBackId, lMin);
            }
            updateMinTime();
            handler.postDelayed(rLoc, Const.lLoc);
        }

        /**
         * 移除位置更新
         *
         * @param iCallBack
         * @return 无
         */
        @Override
        public void removeUpdates(ICallBack iCallBack) throws RemoteException {
            if (iCallBack == null) {
                return;
            }
            if (rcListICallBack.unregister(iCallBack)) {
                locService.setUseCache(false);
                int iCallBackId = iCallBack.asBinder().hashCode();
                Utils.writeCat("server|unregister callback ", iCallBackId);
                saMin.remove(iCallBackId);
            }
            updateMinTime();
        }

        /**
         * 设置使用/禁用GPS(仅在用户勾选了[位置和安全]中的[使用GPS卫星]时本设置有效， 在使用时会启动手机GPS设备)
         *
         * @param bUseGps
         * @return 无
         */
        @Override
        public void useGps(boolean bUseGps) {
            if (locService == null) {
                return;
            }
            if (bUseGps) {
                Utils.writeCat("server|use gps");
            } else {
                Utils.writeCat("server|not use gps");
            }
            locService.setUseGps(bUseGps);
        }

        /**
         * 上传/停止上传ZIP压缩日志
         *
         * @param bUp
         * @return 无
         */
        @Override
        public void upZip(boolean bUp) throws RemoteException {
            locService.setUpZip(bUp);
        }
    };

    @Override
    public void onCreate() {
        if (locService == null) {
            saMin.put(0, Const.lMaxReq);
            //
            locService = LocationServiceFactory.getInstance();
            locService.init(getApplicationContext());
            locService.setUseCache(true);
            locService.setUseGps(false);
            //设置认证串
            locService.setAuth("leador_test##00F02AEB4E8C1295BC6BC8E020DE1033##leadorlocapiserviceserver");
            HandlerThread handlerThread = new HandlerThread(Const.strThreadName);
            handlerThread.start();
            handler = new MyLocationHandler(handlerThread.getLooper());
            updateMinTime();
            rLoc = new Runnable() {
                @Override
                public void run() {
                    getLoc();
                    if (handler == null) {
                        return;
                    }
                    handler.postDelayed(this, Const.lLoc);
                }
            };
            handler.postDelayed(rLoc, Const.lDelay);
            iBindNum = 0;
        }
        Utils.writeCat("server|on create with lib version ", locService.getVersion());
    }

    /**
     * 更新两次定位间的最小时间间隔
     *
     * @return 无
     */
    private synchronized void updateMinTime() {
        long lLastLoc = Const.lLoc;
        for (int i = 0; i < saMin.size(); i++) {
            int iKey = saMin.keyAt(i);
            long lValue = saMin.get(iKey);
            if (i == 0) {
                Const.lLoc = lValue;
            } else if (lValue < Const.lLoc) {
                Const.lLoc = lValue;
            }
        }
        if (Const.lLoc < Const.lMinGps) {
            Const.lLoc = Const.lMinGps;
        } else if (Const.lLoc > Const.lMaxReq) {
            Const.lLoc = Const.lMaxReq;
        }
        if (lLastLoc != Const.lLoc) {
            handler.removeCallbacks(rLoc, null);
            Utils.writeCat("server|set min time ", Const.lLoc, "ms");
        }
    }

    /**
     * 从获取位置
     *
     * @return 无
     */
    private synchronized void getLoc() {
        if (locService == null) {
            return;
        }
        iLocNum++;
        Utils.writeCat("server|get loc#", iLocNum);
        LocationInfo locationInfo = new LocationInfo();
        try {
            locationInfo = locService.getLocation();
        } catch (Exception e) {
            Utils.printE(e);
        }
        updateLoc(locationInfo);
    }

    /**
     * 更新位置
     *
     * @param locationInfo
     */
    private void updateLoc(LocationInfo locationInfo) {
        if (locationInfo == null) {
            notifyAll(null);
            return;
        } else if (locationInfo.getProvider().length() < 1) {
            Utils.writeCat("loc is not null, but provider illegal");
            notifyAll(null);
            return;
        } else if (locationInfo.getLat() == 0d) {
            Utils.writeCat("loc is not null, but lat illegal");
            notifyAll(null);
            return;
        }
        loc = new Location(locationInfo.getProvider());
        loc.setLongitude(locationInfo.getLon());
        loc.setLatitude(locationInfo.getLat());
        loc.setAccuracy(locationInfo.getAccuracy());
        loc.setSpeed(locationInfo.getSpeed());
        loc.setBearing(locationInfo.getBearing());
        loc.setTime(locationInfo.getTime());
        //
        Bundle bundle = new Bundle();
        bundle.putString("type", locationInfo.getType());
        bundle.putString("retype", locationInfo.getRetype());
        bundle.putString("citycode", locationInfo.getCitycode());
        bundle.putString("desc", locationInfo.getDesc());
        bundle.putString("adcode", locationInfo.getAdcode());
        //
        loc.setExtras(bundle);
        //
        notifyAll(getLocStr());
    }

    /**
     * 获取位置字符串
     *
     * @return 无
     */
    private String getLocStr() {
        JSONObject json = new JSONObject();
        try {
            json.put("provider", loc.getProvider());
            json.put("longitude", loc.getLongitude());
            json.put("latitude", loc.getLatitude());
            json.put("accuracy", loc.getAccuracy());
            json.put("speed", loc.getSpeed());
            json.put("bearing", loc.getBearing());
            json.put("time", loc.getTime());
            Bundle bundle = loc.getExtras();
            //
            json.put("type", bundle.getString("type"));
            json.put("retype", bundle.getString("retype"));
            json.put("citycode", bundle.getString("citycode"));
            json.put("desc", bundle.getString("desc"));
            json.put("adcode", bundle.getString("adcode"));
        } catch (Exception e) {
            Utils.printE(e);
        }
        return json.toString();
    }

    /**
     * 通知位置
     *
     * @param strLoc
     */
    private synchronized void notifyAll(String strLoc) {
        if (locService == null) {
            return;
        }
        if (rcListICallBack == null) {
            return;
        }
        autoFren();
        final int iClients = rcListICallBack.beginBroadcast();
        Utils.writeCat("server|notify ", iClients, " clients");
        if (iClients == 0) {
            if (locService == null) {
                return;
            }
            locService.setUseCache(false);
        }
        for (int i = 0; i < iClients; i++) {
            try {
                rcListICallBack.getBroadcastItem(i).onLocationChanged(strLoc);
            } catch (Exception e) {
                Utils.printE(e);
            }
        }
        rcListICallBack.finishBroadcast();
    }

    /**
     * 智能定位频率
     *
     */
    private void autoFren() {
        if (loc == null) {
            return;
        }
        if (loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
            if (Const.lLoc >= Const.lMinNet) {
                return;
            }
            Utils.writeCat("server|slow down net loc");
            Const.lLoc = Const.lMinNet;
            Utils.writeCat("server|set min time ", Const.lLoc, "ms");
        } else if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
            updateMinTime();
        }
    }

    @Override
    public void onStart(Intent intent, int iStartId) {
        Utils.writeCat("server|on start with id ", String.valueOf(iStartId));
    }

    @Override
    public IBinder onBind(Intent intent) {
        iBindNum++;
        handler.sendEmptyMessage(1);
        Utils.writeCat("server|on bind, contain ", iBindNum, " binds");
        Utils.toast(getApplicationContext(), "in debug mode, only for test");
        return stub;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        iBindNum--;
        Utils.writeCat("server|on unbind, contain ", iBindNum, " binds");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        Utils.writeCat("server|on destroy");
        handler.removeCallbacks(rLoc, null);
        handler = null;
        rLoc = null;
        rcListICallBack.kill();
        saMin.clear();
        //
        locService.destroy();
        locService = null;
        super.onDestroy();
    }

    @Override
    public void onRebind(Intent intent) {
        Utils.writeCat("server|on rebind");
        super.onRebind(intent);
    }

    /**
     *
     */
    private static class MyLocationHandler extends Handler {
        private MyLocationHandler() {
            //
        }

        private MyLocationHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            //
        }
    }
}